package compile;

import javax.imageio.stream.ImageInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

//通过这个类来创建进程,并执行java,javac等命令
public class CommandUtil {
    //用于创建进程并执行命令
    //cmd表示要执行的命令 比如javac
    //stdOutFile表示指定标准输出写到那个文件中
    //stderrFile表示指定标准错误写到哪个文件中
    //Runtime用于创建进程
    public static int run(String cmd, String stdOutFile, String stderrFile)
            throws IOException, InterruptedException {
        //  Runtime runtime = new Runtime;  不需要自己创建实例,
        // java对多进程的操作是比较粗糙的,不方便精细化操作
        Process process = Runtime.getRuntime().exec(cmd);//单例模式,Runtime的实例只有一个
        // process 表示新创建的进程  命令相当于exe程序的路径
        //当新进程创建好以后和当前进程并发运行
        //新进程跑起来后,需要获取新进程的输出结果
        if (stdOutFile != null) {
            InputStream stdoutFrom = process.getInputStream();
            // InputStream 是FileInputStream的父类
            // 通过这个对象 读取到当前新进程标准输出的内容
            FileOutputStream stdoutTo = new FileOutputStream(stdOutFile);
            //两个对象 ,一个是标准输出用于比对,一个是用户输入
            //接下来从新进程依次读取每个字节,写到stdoutTo 这个文件里
            while(true){
                int ch = stdoutFrom.read();//读取文件每个字节
                if (ch == -1){
                    break; // 判断文件是否读取完毕
                }
                stdoutTo.write(ch);//输出每个字节
            }

        //关闭文件
            stdoutFrom.close();
            stdoutTo.close();

            //23-38行的目的是重定向,将原本应该输出在显示屏的内容输出到文件里

        }
        //接下来对标准错误重定向
        if(stderrFile != null){
            // getErrorStream 得到的是标准错误
            InputStream stderrFrom = process.getErrorStream();
            FileOutputStream stderrTo = new FileOutputStream(stderrFile);
            while(true){
                int ch = stderrFrom.read();
                if( ch == -1){
                    break;
                }
                stderrTo.write(ch);
            }
            stderrFrom.close();
            stderrTo.close();

        }
// 总结:调用该run方法一共做了4件事儿
        //1,调用Runtime.exec 创建子进程,其中创建进程的进程成为父进程,被创建的进程称为子进程
        //2,针对子进程的标准输出进行重定向,写到一个文件里
        //3,针对子进程的标准错误进行重定向,也写到一个文件里
        //(标准输入也可以重定向此处不需要)
        //4,父进程等待子进程结束,并获取到子进程的退出码 waiFor()方法

        // 因为父进程和子进程的执行顺序是凭借操作系统的调度的,所以父进程要想获取到子进程的退出码
        //必须要保证子进程执行完毕.waitFor这个方法会  阻塞式  的等待父进程执行到waitFor后
        //会暂停,等待子进程执行完毕才返回,获取子进程的退出码
        int exitCode = process.waitFor();
        return exitCode;
    }

    public static void main(String[] args) {
        try {
         int ret =  CommandUtil.run("javac","./stdout.txt","./stderr.txt");
            System.out.println(ret);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//当前的CommandUtil只能创建子进程执行某个命令,真正要实现编译运行代码功能,
// 就需要完成编译,运行,获取结果等多个环节
